package com.inibb.app.service.security.impl;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.inibb.app.converter.Db2VOConverter;
import com.inibb.app.converter.UserConverter;
import com.inibb.app.dao.JpaGenericDao;
import com.inibb.app.dao.api.IDao;
import com.inibb.app.exception.ServiceVLDException;
import com.inibb.app.pagination.dto.PaginationCriteria;
import com.inibb.app.po.security.Role;
import com.inibb.app.po.security.User;
import com.inibb.app.service.security.IUserService;
import com.inibb.app.util.DateUtils;
import com.inibb.app.util.SecurityUtils;
import com.inibb.app.util.lang.StringUtil;
import com.inibb.app.vo.UserSC;
import com.inibb.app.vo.security.UserVO;



@Service("userService")
@Transactional
public class UserServiceImpl implements IUserService{
	
//	private SimpleHibernateTemplate<Role, Integer> roleDao;
//	private SimpleHibernateTemplate<User, Integer> userDao;
	private IDao<Role, Integer> roleDao;
	private IDao<User, Integer> userDao;
	@PersistenceContext(unitName = "entityManager")
	private EntityManager entityManager ;
	
	@Autowired
	public void setEntityManagerFactory() {
		roleDao = new JpaGenericDao<Role, Integer>(entityManager, Role.class);
		userDao = new JpaGenericDao<User, Integer>(entityManager, User.class);
	}

	private UserConverter converter = new UserConverter();
	
	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Transactional(readOnly = true)
	public UserVO getUserByLoginName(String loginName) {
		UserSC paginationCriteria = new UserSC();
		paginationCriteria.setStartRow(0);
		paginationCriteria.setPageSize(Integer.MAX_VALUE);
		paginationCriteria.setLoginName(loginName);
		
		List<Object[]> list = (List<Object[]>) userDao.queryNativeSqlForList("admin", "searchPageUser", paginationCriteria);
		if(CollectionUtils.isEmpty(list))
			return null;
//		List<UserVO> gcList = new ArrayList<UserVO>();
		return Db2VOConverter.toVO4User(list.get(0));
		
//		DetachedCriteria criteria = DetachedCriteria.forClass(User.class);
//		criteria.add(Restrictions.eq("loginName", loginName));
//		List<User> usrs = (List<User>)userDao.getHibernateTemplate().findByCriteria(criteria);
//		if(usrs!=null && usrs.size()==1)
//			return converter.toDTO(usrs.get(0));
//		else
//			return null;
	}

	public void save (UserVO userDTO) throws ServiceVLDException{
		
		userDTO.setRegDate(DateUtils.getSystemDate());

//		Set<ConstraintViolation<UserVO>> errs = validator.validate(userDTO);
//		if(errs.isEmpty()){
			User po = converter.toPO(userDTO);
			po.getRoles().clear();
			for(String rid:userDTO.getRoleIds()){
				if(!StringUtil.isEmptyString(rid)){
					Role r = roleDao.get(Integer.parseInt(rid));
					po.getRoles().add(r);
				}
			}
			po.setPassword(SecurityUtils.createPasswordHash(po.getPassword()));
			userDao.persist(po);
//		}else{
//			ServiceVLDException exp = JpaValidatorHelper.convertToException(errs, userDTO, "user");
//			throw exp;
//		}
	}

	public void update (UserVO userDTO)  throws ServiceVLDException{


//		Set<ConstraintViolation<UserVO>> errs = validator.validate(userDTO);
//		if(errs.isEmpty()){
			User po = converter.toPO(userDTO);
			po.getRoles().clear();
			for(String rid:userDTO.getRoleIds()){
				if(!StringUtil.isEmptyString(rid)){
					Role r = roleDao.get(Integer.parseInt(rid));
					po.getRoles().add(r);
				}
			}
			userDao.merge(po);
//		}else{
//			ServiceVLDException exp = JpaValidatorHelper.convertToException(errs, userDTO, "user");
//			throw exp;
//		}

	}

	public void delete (UserVO userDTO) {
		userDao.delete( userDTO.getId() );
	}

	@Override
	public int countUsers(UserSC paginationCriteria) {
		Integer total = (Integer) userDao.queryNativeSqlForObject("admin", "countPageUser", paginationCriteria);
		
		if(total==null)
			return 0;
		else			
			return total;
	}

	@Override
	public Collection<UserVO> searchUsers(UserSC paginationCriteria) {
		List<Object[]> list = (List<Object[]>) userDao.queryNativeSqlForList("admin", "searchPageUser", paginationCriteria);
		List<UserVO> gcList = new ArrayList<UserVO>();
		for(Object[] obj:list){
			gcList.add(Db2VOConverter.toVO4User(obj));
		}
		return gcList;
	}

	@Override
	public int countUsers(PaginationCriteria paginationCriteria) {
		BigInteger total = (BigInteger) userDao.queryNativeSqlForObject("admin", "countPageUser", paginationCriteria);
		
		if(total==null)
			return 0;
		else			
			return total.intValue();
	}

	@Override
	public Collection<UserVO> searchUsers(PaginationCriteria paginationCriteria) {
		List<Object[]> list = (List<Object[]>) userDao.queryNativeSqlForList("admin", "searchPageUser", paginationCriteria);
		List<UserVO> gcList = new ArrayList<UserVO>();
		for(Object[] obj:list){
			gcList.add(Db2VOConverter.toVO4User(obj));
		}
		return gcList;
	}
	
	public Collection<UserVO> getAll() {
		return new UserConverter().toDTOs(userDao.getAll());
//		UserSC paginationCriteria = new UserSC();
//		paginationCriteria.setStartRow(0);
//		paginationCriteria.setPageSize(Integer.MAX_VALUE);
//		List<Object[]> list = (List<Object[]>) userDao.queryNativeSqlForList("admin", "searchPageUser", new PaginationCriteria(0,Integer.MAX_VALUE));
//		List<UserVO> gcList = new ArrayList<UserVO>();
//		for(Object[] obj:list){
//			gcList.add(Db2VOConverter.toVO4User(obj));
//		}
//		return gcList;
	}
	
	@SuppressWarnings("unchecked")
	public Collection<UserVO> getAllByRoleId(final String roleId) {
		Map<String, Object> criteria = new HashMap<String, Object>();
		criteria.put("roleId", roleId);
		
		List<Object[]> list = (List<Object[]>) userDao.queryNativeSqlForList("admin", "searchUserByRoleId", criteria);
		List<UserVO> vos = new ArrayList<UserVO>();
		for(Object[] obj:list){
			vos.add(Db2VOConverter.toVO4User(obj));
		}
		return vos;
		
//		return converter.toDTOs( userDao.getHibernateTemplate().executeFind(new HibernateCallback<List<User>>(){
//			@Override
//			public List<User> doInHibernate(Session session){
//				Query query = session.createQuery("SELECT u FROM User u inner join u.roles r WHERE r.id=:roleId");
//				query.setParameter("roleId", Long.valueOf(roleId));
//				List<User> users = query.list();
//				return users;
//			}
//		}));
	}
	public UserVO getUserById(final String id) {
		return converter.toDTO(userDao.get(Integer.parseInt(id)));
	}
	
	/*@Override
	public Collection<UserVO> searchUsersWithRoleId(final 
			PaginationCriteria paginationCriteria, final String roleId) {
		List<User> ls = userDao.getHibernateTemplate().execute(new HibernateCallback<List<User>>(){

			@Override
			public List<User> doInHibernate(Session session)
					throws HibernateException, SQLException {
				Criteria criteria = session.createCriteria(User.class);
				criteria.createAlias("roles", "roles", CriteriaSpecification.INNER_JOIN);

				HibernateCriteriaUtils.getHibernateFilterAndSorterCriteria(criteria, paginationCriteria);
				criteria.setFirstResult(paginationCriteria.getRowStart());
				criteria.setMaxResults(paginationCriteria.getRowEnd()-paginationCriteria.getRowStart());
				criteria.add(Restrictions.eq("roles.id", Long.valueOf(roleId)));
				return (List<User>)criteria.list();
			}
			
		});
		
		return converter.toDTOs(ls);
	}*/
	
	/*@Override
	public int countUsersWithRoleId(final 
			PaginationCriteria paginationCriteria, final String roleId) {
		Integer count = userDao.getHibernateTemplate().execute(new HibernateCallback<Integer>(){

			@Override
			public Integer doInHibernate(Session session)
					throws HibernateException, SQLException {
				Criteria criteria = session.createCriteria(User.class);
				criteria.createAlias("roles", "roles", CriteriaSpecification.INNER_JOIN);
				
				HibernateCriteriaUtils.getHibernateFilterCriteria(criteria, paginationCriteria);
				criteria.add(Restrictions.eq("roles.id", Long.valueOf(roleId)));
				criteria.setProjection(Projections.rowCount());
				
				return (Integer)criteria.uniqueResult();
			}
			
		});
		
		if(count==null) 
			return 0;
		else
			return count;
	}*/

	@Override
	public void changeActiveFlag(String userId) throws ServiceVLDException {
		User user = userDao.get(Integer.parseInt(userId));
		if("Y".equals(user.getActiveFlag())){
			user.setActiveFlag("N");
			logger.debug("user {} was disabled.", user.getId());
		}else{
			user.setActiveFlag("Y");
			logger.debug("user {} was enabled.", user.getId());
		}
		this.userDao.merge(user);
	}

	@Override
	public void delete(String userId) {
		this.userDao.delete(Integer.parseInt(userId));	
	}

	@Override
	public void chanagePassword(UserVO userDTO) throws ServiceVLDException {
//		Set<ConstraintViolation<UserVO>> errs = validator.validate(userDTO);
//		if(errs.isEmpty()){
			User user = userDao.get(userDTO.getId());
			
			user.setPassword(SecurityUtils.createPasswordHash(userDTO.getPassword()));
			this.userDao.merge(user);
//		}else{
//			ServiceVLDException exp = JpaValidatorHelper.convertToException(errs, userDTO, "user");
//			throw exp;
//		}

	}
	
	public boolean checkLoginNameExists(final String loginName) {
		UserVO user = getUserByLoginName(loginName);
		if(user==null)
			return false;
		else
			return true;
//		Integer count = userDao.getHibernateTemplate().execute(new HibernateCallback<Integer>(){
//
//			@Override
//			public Integer doInHibernate(Session session)
//					throws HibernateException, SQLException {
//				Criteria criteria = session.createCriteria(User.class);
//				criteria.add(Restrictions.eq("loginName", loginName));
//				criteria.setProjection(Projections.rowCount());
//				
//				return (Integer)criteria.uniqueResult();
//			}
//			
//		});
//		
//		if(count==null||count==0) 
//			return false;
//		else
//			return true;
	}
	
	public boolean checkEmailExists(final String email) {
		UserSC paginationCriteria = new UserSC();
		paginationCriteria.setEmail(email);
		
		BigInteger total = (BigInteger) userDao.queryNativeSqlForObject("admin", "countPageUser", paginationCriteria);
		
		if(total==null||total.intValue()==0)
			return false;
		else			
			return true;
		
//		Integer count = userDao.getHibernateTemplate().execute(new HibernateCallback<Integer>(){
//
//			@Override
//			public Integer doInHibernate(Session session)
//					throws HibernateException, SQLException {
//				Criteria criteria = session.createCriteria(User.class);
//				criteria.add(Restrictions.eq("email", email));
//				criteria.setProjection(Projections.rowCount());
//				
//				return (Integer)criteria.uniqueResult();
//			}
//			
//		});
//		
//		if(count==null||count==0) 
//			return false;
//		else
//			return true;
	}

	@Override
	public List<String> searchGrantedUrls(Integer userId) {
		Map<String, Object> criteria = new HashMap<String, Object>();
		criteria.put("userId", userId);
		List<String> list = (List<String>) userDao.queryNativeSqlForList("admin", "searchGrantedUrls", criteria);
		return list;
	}
}
